
package org.apache.solr.core;

import java.util.*;
import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableMap;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.util.DOMUtil;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * An Object which represents a Plugin of any type 
 *
 */
public class PluginInfo {

    public final String name, className, type;
    public final NamedList initArgs;
    public final Map<String, String> attributes;
    public final List<PluginInfo> children;

    public PluginInfo(String type, Map<String, String> attrs, NamedList initArgs, List<PluginInfo> children) {
        this.type = type;
        this.name = attrs.get("name");
        this.className = attrs.get("class");
        this.initArgs = initArgs;
        attributes = unmodifiableMap(attrs);
        this.children = children == null ? Collections.<PluginInfo>emptyList() : unmodifiableList(children);
    }

    public PluginInfo(Node node, String err, boolean requireName, boolean requireClass) {
        type = node.getNodeName();
        name = DOMUtil.getAttr(node, "name", requireName ? err : null);
        className = DOMUtil.getAttr(node, "class", requireClass ? err : null);
        initArgs = DOMUtil.childNodesToNamedList(node);
        attributes = unmodifiableMap(DOMUtil.toMap(node.getAttributes()));
        children = loadSubPlugins(node);
    }

    private List<PluginInfo> loadSubPlugins(Node node) {

        List<PluginInfo> lchildren = new ArrayList<>();
        //if there is another sub tag with a non namedlist tag that has to be another plugin
        NodeList nlst = node.getChildNodes();
        for (int i = 0; i < nlst.getLength(); i++) {
            Node nd = nlst.item(i);
            if (nd.getNodeType() != Node.ELEMENT_NODE) {
                continue;
            }
            if (NL_TAGS.contains(nd.getNodeName())) {
                continue;
            }
            PluginInfo pluginInfo = new PluginInfo(nd, null, false, false);
            if (pluginInfo.isEnabled()) {
                lchildren.add(pluginInfo);
            }
        }
        return lchildren.isEmpty() ? Collections.<PluginInfo>emptyList() : unmodifiableList(lchildren);
    }

    @Override
    public String toString() {

        StringBuilder sb = new StringBuilder("{");
        if (type != null) {
            sb.append("type = ").append(type).append(",");
        }
        if (name != null) {
            sb.append("name = ").append(name).append(",");
        }
        if (className != null) {
            sb.append("class = ").append(className).append(",");
        }
        if (initArgs.size() > 0) {
            sb.append("args = ").append(initArgs);
        }
        sb.append("}");

        return sb.toString();
    }

    public boolean isEnabled() {
        String enable = attributes.get("enable");
        return enable == null || Boolean.parseBoolean(enable);
    }

    public boolean isDefault() {
        return Boolean.parseBoolean(attributes.get("default"));
    }

    public PluginInfo getChild(String type) {
        List<PluginInfo> l = getChildren(type);
        return l.isEmpty() ? null : l.get(0);
    }

    /**
     * Filter children by type
     *
     * @param type The type name. must not be null
     * @return The mathcing children
     */
    public List<PluginInfo> getChildren(String type) {
        if (children.isEmpty()) {
            return children;
        }
        List<PluginInfo> result = new ArrayList<>();
        for (PluginInfo child : children) {
            if (type.equals(child.type)) {
                result.add(child);
            }
        }
        return result;
    }

    public static final PluginInfo EMPTY_INFO = new PluginInfo("", Collections.<String, String>emptyMap(), new NamedList(), Collections.<PluginInfo>emptyList());
    private static final HashSet<String> NL_TAGS = new HashSet<>(Arrays.asList("lst", "arr", "bool", "str", "int", "long", "float", "double"));
}
